diff --git a/data/book.png b/data/book.png
new file mode 100644
index 0000000000000000000000000000000000000000..176fb6aa96154614c7edb3a7ed80064d23a8fceb
Binary files /dev/null and b/data/book.png differ
diff --git a/data/bookshelf.png b/data/bookshelf.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ecc50ff3ad1d0f633f2fb18cd59801fa2391ed7
Binary files /dev/null and b/data/bookshelf.png differ
diff --git a/data/paper.png b/data/paper.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae5c06bc6fea52d8d78c85592b97405682bf0e9b
Binary files /dev/null and b/data/paper.png differ
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 3f83c74192caf9a7c79e281abb21c474162dd53d..cb398a537527a436c968953a866a6591bfd06a7e 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -156,6 +156,10 @@ video::ITexture * CraftItem::getImage()
 
 	if(m_subname == "Stick")
 		name = "stick.png";
+	else if(m_subname == "paper")
+		name = "paper.png";
+	else if(m_subname == "book")
+		name = "book.png";
 	else if(m_subname == "lump_of_coal")
 		name = "lump_of_coal.png";
 	else if(m_subname == "lump_of_iron")
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 954c85f2f7d9e75319ed9249a214525524f7bc9f..7e97a8d04e22ba2e85688a9bb37d3e018585da01 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -251,6 +251,17 @@ void init_mapnode()
 	f->solidness = 0; // drawn separately, makes no faces
 	f->walkable = false;
 
+ 	i = CONTENT_BOOKSHELF;
+ 	f = &g_content_features[i];
+ 	f->setAllTextures("bookshelf.png");
+	f->setTexture(0, "wood.png");
+	f->setTexture(1, "wood.png");
+	// FIXME: setInventoryTextureCube() only cares for the first texture
+	f->setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png");
+	//f->setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png");
+	f->param_type = CPT_MINERAL;
+	f->is_ground_content = true;
+
 	i = CONTENT_GLASS;
 	f = &g_content_features[i];
 	f->light_propagates = true;
diff --git a/src/mapnode.h b/src/mapnode.h
index 52d0199c4de760ab372b409a46d00cd76674b29e..57335b74117ad159594e35f1d167cc99760cef0a 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -106,6 +106,7 @@ void init_content_inventory_texture_paths();
 #define CONTENT_BRICK 24
 #define CONTENT_CLAY 25
 #define CONTENT_PAPYRUS 26
+#define CONTENT_BOOKSHELF 27
 
 /*
 	Content feature list
diff --git a/src/materials.cpp b/src/materials.cpp
index e95ca7ba9c66c643c11ae4df88a8665053b9ceea..7815f593ebecaa36d27ef3872f486efc9893c63e 100644
--- a/src/materials.cpp
+++ b/src/materials.cpp
@@ -80,6 +80,7 @@ void initializeMaterialProperties()
 	setWoodLikeDiggingProperties(CONTENT_GLASS, 0.15);
 	setWoodLikeDiggingProperties(CONTENT_FENCE, 0.75);
 	setWoodLikeDiggingProperties(CONTENT_WOOD, 0.75);
+	setWoodLikeDiggingProperties(CONTENT_BOOKSHELF, 0.75);
 	setWoodLikeDiggingProperties(CONTENT_CHEST, 1.0);
 
 	g_material_properties[CONTENT_SIGN_WALL].setDiggingProperties("",
diff --git a/src/server.cpp b/src/server.cpp
index e9875456cfc8b6835dd7d4003b8a7746af5469a4..f40ed05a5e76fe4a62f27db300a68c83e10b0c48 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -4017,6 +4017,54 @@ void Server::UpdateCrafting(u16 peer_id)
 					found = true;
 				}
 			}
+
+			// Paper
+			if(!found)
+			{
+				ItemSpec specs[9];
+				specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+				specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+				specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+				if(checkItemCombination(items, specs))
+				{
+					rlist->addItem(new CraftItem("paper", 1));
+					found = true;
+				}
+			}
+
+			// Book
+			if(!found)
+			{
+				ItemSpec specs[9];
+				specs[1] = ItemSpec(ITEM_CRAFT, "paper");
+				specs[4] = ItemSpec(ITEM_CRAFT, "paper");
+				specs[7] = ItemSpec(ITEM_CRAFT, "paper");
+				if(checkItemCombination(items, specs))
+				{
+					rlist->addItem(new CraftItem("book", 1));
+					found = true;
+				}
+			}
+
+			// Book shelf
+			if(!found)
+			{
+				ItemSpec specs[9];
+				specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				specs[3] = ItemSpec(ITEM_CRAFT, "book");
+				specs[4] = ItemSpec(ITEM_CRAFT, "book");
+				specs[5] = ItemSpec(ITEM_CRAFT, "book");
+				specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+				if(checkItemCombination(items, specs))
+				{
+					rlist->addItem(new MaterialItem(CONTENT_BOOKSHELF, 1));
+					found = true;
+				}
+			}
 		}
 	
 	} // if creative_mode == false
@@ -4112,6 +4160,7 @@ void setCreativeInventory(Player *player)
 		CONTENT_LEAVES,
 		CONTENT_CACTUS,
 		CONTENT_PAPYRUS,
+		CONTENT_BOOKSHELF,
 		CONTENT_GLASS,
 		CONTENT_FENCE,
 		CONTENT_MESE,
diff --git a/src/tile.cpp b/src/tile.cpp
index c77262c49ee7bf4185834b7cb10e37c60697233d..c703e147c17de597d4195f7ca5da8b26a4a936a0 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -518,6 +518,7 @@ void TextureSource::buildMainAtlas()
 	sourcelist.push_back("cactus_side.png");
 	sourcelist.push_back("cactus_top.png");
 	sourcelist.push_back("papyrus.png");
+	sourcelist.push_back("bookshelf.png");
 	sourcelist.push_back("glass.png");
 	sourcelist.push_back("mud.png^grass_side.png");
 	sourcelist.push_back("cobble.png");