2013-04-08 46 views
5

मैंने देखा कि बूस्ट भावना कुछ प्रश्नों को यहां प्रदान करती है, इसलिए एक उपयोगकर्ता यहां बूस्ट भावना और अन्य उपयोगकर्ता के बारे में मदद मांग रहा है, जिसने उत्तर दिया है कि बढ़ावा देने की भावना बयान के साथ अच्छी तरह से काम करती है, न कि "जेनेरिक टेक्स्ट" (मुझे खेद है अगर मुझे इसे सही तरीके से याद नहीं है)।बूस्ट भावना पोस्टस्क्रिप्ट/पीडीएफ जैसी भाषाओं को संभाल सकती है?

अब मैं टोकन के संदर्भ में पोस्टस्क्रिप्ट और पीडीएफ के बारे में सोचना चाहता हूं और इस तरह के प्रारूपों के लिए अपना दृष्टिकोण सरल बनाना चाहता हूं, समस्या यह है कि पीडीएफ एक मार्कअप भाषा और एक प्रोग्रामिंग भाषा के बीच मिश्रण है जो कूदता है और इसमें टेबल, और मैं एक्सएमएल, सी ++ कोड और अन्य भाषाओं और प्रारूपों जैसे सबसे लोकप्रिय फ़ाइल प्रारूपों पर विचार करते समय कुछ इसी तरह के बारे में नहीं सोच सकता।

एक और तथ्य भी है: मैं वास्तव में उन लोगों को नहीं ढूंढ सकता जिनके पास बूस्ट :: भावना के साथ किसी प्रकार का अनुभव था जो पीडीएफ पार्सर या लेखक की इच्छा रखते थे, इसलिए मैं पूछ रहा हूं, उत्साह :: भावना जो इसे पार्स करने में सक्षम है पीडीएफ फाइल और तत्वों को टोकन के रूप में आउटपुट करते हैं?

+0

मुझे एक स्तर -1 स्कैनर के लिए सी-कोड मिला है (कोई <<>>) [यहां] (https://groups.google.com/d/msg/comp.lang.postscript/XbxHv5rcFxc/OetXbfI4PQYJ) और पोस्टस्क्रिप्ट के लिए आंशिक अनुवाद [यहां] (https://groups.google.com/d/msg/comp.lang.postscript/u4QmuQZhrxU/LNF_r0PWX1EJ)। –

+0

एक और एक (पोस्टस्क्रिप्ट में) मिला [यहां] (https://groups.google.com/d/msg/comp.windows.news/g1fs5ajR1YQ/FgW3DFKx0dUJ)। –

उत्तर

11

हालांकि बूस्ट के साथ इसका कोई लेना-देना नहीं है, मैं आपको आश्वस्त करता हूं कि पीडीएफ (और पोस्टस्क्रिप्ट) का पार्सिंग जितना छोटा हो उतना छोटा हो सकता है। मान लीजिए कि आपके पास स्कैनर ऑब्जेक्ट है जो टोकन की एक श्रृंखला देता है। टोकन प्रकार आप स्कैनर से मिल जाएगा रहे हैं:

  • स्ट्रिंग
  • Dict शुरू (< <)
  • Dict एंड (>>)
  • नाम (/ जो कुछ भी)
  • संख्या
  • हेक्स सरणी
  • बाएं कोण (<)
  • दायां कोण (>)
  • सरणी शुरू ([)
  • सरणी अंत (])
  • प्रक्रिया शुरू ({)
  • प्रक्रिया अंत (})
  • टिप्पणी (% foo)
  • वर्ड

मेरा स्कैनर स्टार्ट, टिप्पणी, स्ट्रिंग, हेक्सएरे, टोकन, डिक्टेंड और होन के लिए राज्यों के साथ एक परिमित-राज्य ऑटोमाटा है।

जिस तरह से आप पीडीएफ पार्स करते हैं, उसे पार्सिंग नहीं करते हैं, लेकिन इसे निष्पादित करके। इन टोकन को देखते हुए, मेरी "पार्सर" इस ​​तरह दिखता है (सी # में):

while (true) { 
    MLPdfToken = scanner.GetToken(); 
    if (token == null) 
     return MachineExit.EndOfFile; 
    PdfObject obj = PdfObject.FromToken(token); 
    PdfProcedure proc = obj as PdfProcedure; 

    if (proc != null) 
    { 
     if (IsExecuting()) 
     { 
      if (token.Type == PdfTokenType.RBrace) 
       proc.Execute(this); 
      else 
       Push(obj); 
     } 
     else { 
      proc.Execute(this); 
     } 
     if (proc.IsTerminal) 
      return Machine.ParseComplete; 
    } 
    else { 
     Push(obj); 
    } 
} 

मैं भी जोड़ देंगे कि अगर आप दे हर PdfObject एक निष्पादित() विधि है ऐसी है कि आधार वर्ग कार्यान्वयन machine.Push(this) और IsTerminal कि false देता है, आरईपीएल हो जाता है आसान:

while (true) { 
    MLPdfToken = scanner.GetToken(); 
    if (token == null) 
     return MachineExit.EndOfFile; 
    PdfObject obj = PdfObject.FromToken(token); 

    if (IsExecuting()) 
    { 
     if (token.Type == PdfTokenType.RBrace) 
      obj.Execute(this); 
     else 
      Push(obj); 
    } 
    else { 
     obj.Execute(this); 
     if (obj.IsTerminal) 
      return Machine.ParseComplete;     
    } 
} 

और अधिक समर्थन नहीं है मशीन में - मशीन PdfObject के ढेर और यह तक पहुँचने के लिए कुछ तरीकों (पुश, पॉप, मार्क, CountToMark, सूचकांक, Dup, स्वैप) है, साथ ही ExecProcBegin और ExecProcEnd।

इसके अलावा, यह बहुत हल्का है। एकमात्र चीज जो थोड़ा अजीब है वह है कि PdfObject.FromToken एक टोकन लेता है और यदि यह एक आदिम प्रकार (संख्या, स्ट्रिंग, नाम, हेक्स, बूल) एक संबंधित पीडीएफ ऑब्जेक्ट देता है।अन्यथा, यह दिया गया टोकन लेता है और PdfProcedure ऑब्जेक्ट्स से जुड़े प्रक्रिया नामों के "प्रो सेट" शब्दकोश में दिखता है। तो जब आप टोकन << कि एक proc सेट में देखा और इस कोड के साथ आता है जाता है का सामना:

void DictBegin(PdfMachine machine) 
{ 
    machine.Push(new PdfMark(PdfMarkType.Dictionary)); 
} 

तो << वास्तव में अर्थ है "एक शब्दकोश की शुरुआत के रूप में ढेर निशान >> और अधिक दिलचस्प हो जाता है।:

void DictEnd(PdfMachine machine) 
{ 
    PdfDict dict = new PdfDict(); 
    // PopThroughMark pops the entire stack up to the first matching mark, 
    // throws an exception if it fails. 
    PdfObject[] arr = machine.PopThroughMark(PdfMarkType.Dictionary); 
    if ((arr.Length & 1) != 0) 
     throw new PdfException("dictionaries need an even number of objects."); 
    for (int i=0; i < arr.Length; i += 2) 
    { 
     PdfObject key = arr[i], val = arr[i + 1]; 
     if (key.Type != PdfObjectType.Name) 
      throw new PdfException("dictionaries need a /name for the key."); 
     dict.put((PdfName)key, val); 
    } 
    machine.Push(dict); 
} 

तो >> पॉप एक सरणी में निकटतम शब्दकोश मार्क अप करने के लिए तो शब्दकोश में प्रत्येक जोड़ी डालता है। अब, मैं इस सरणी का आवंटन बिना कर सकता था। मैं तो बस पॉप जोड़े कर सकते थे, उन्हें शब्दकोश में डाल जब तक कि मैं या तो निशान हिट नहीं करता, कोई नाम प्राप्त करने में विफल रहता है या नहीं ढेर अंडरफ्लो।

महत्वपूर्ण अधिग्रहण यह है कि वास्तव में पीडीएफ में कोई वाक्यविन्यास नहीं है, न ही पोस्टस्क्रिप्ट में कोई भी है। कम से कम उतना नहीं जितना आप नोटिस करेंगे। एकमात्र असली सिंटेक्स (और रीड-इवल- (पुश) लूप इसे दिखाता है) '}' है।

तो जब आप इस एक पीडीएफ 14 0 obj << /Type /Annot /SubType /Square >> endobj है कि आपके वास्तव में देखकर प्रक्रियाओं की एक श्रृंखला है:

  1. पुश 14
  2. पुश 0
  3. निष्पादित obj (दो नंबर पॉप और एक "परिभाषा" धक्का वस्तु)।
  4. (निष्पादित शब्दकोश शुरू
  5. पुश/प्रकार
  6. पुश/Annot
  7. पुश/उपप्रकार
  8. पुश/स्क्वायर
  9. निष्पादित शब्दकोश अंत
  10. endobj निष्पादित (ऊपर वस्तु पॉप और फिर मिल पॉप नहीं) अगला। यदि दूसरा परिभाषा है, तो पहले ऑब्जेक्ट पर अपना "मान" सेट करें, और फेंक दें)।

चूंकि "एंडोब" टर्मिनल है, पार्सिंग समाप्त होता है और स्टैक का शीर्ष परिणाम होता है।

तो जब आपको पीडीएफ में ऑब्जेक्ट 14 देखने के लिए कहा जाता है, तो क्रॉस-रेफरेंस टेबल आपको बताती है कि कहां खोजना है, आप उस स्थान पर स्ट्रीम पॉइंटर के साथ एक नई मशीन बनाते हैं और इसे चलाते हैं। यदि ढेर का शीर्ष एक "परिभाषा" वस्तु है, तो आप सफल हुए हैं।

बारे में अब आप हिला दिया जाना चाहिए लेकिन मुझे भरोसा करने नहीं, जब से तुम पीडीएफ धाराओं, जो इस तरह दिखेगा के बारे में सोच रहे हैं:

<< [/key value]* >> stream ...raw data... endstream endobj 

फिर, वहाँ कोई वाक्य रचना है। Proc stream स्टैक के शीर्ष पर दिखता है, जो एक पीडीएफडीक्ट होना चाहिए। यदि ऐसा है, तो यह अगली न्यूलाइन (स्कैनर यह करता है) तक वर्णों का उपभोग करता है, स्ट्रीम में वर्तमान फ़ाइल स्थिति को स्ट्रीम के रूप में संग्रहीत करता है, धारा से स्ट्रीम की लंबाई को पढ़ता है (जो किसी अन्य मशीन को नया करने का कारण बन सकता है), और छोड़ देता है स्ट्रीम के अंत से पहले और स्टैक पर नई स्ट्रीम ऑब्जेक्ट को धक्का देता है। एंडस्ट्रीम एक नो-ऑप है। पीडीएफडीक्ट और पीडीएफस्ट्रीम के बीच एकमात्र अंतर यह है कि पीडीएफस्ट्रीम की शुरुआत स्थिति होती है और एक बूल कहता है कि यह एक धारा है, अन्यथा मैं वस्तु का दोहरा उद्देश्य करता हूं।

पोस्टस्क्रिप्ट लगभग समान है सिवाय इसके कि निष्पादन वातावरण थोड़ा अधिक जटिल है।उदाहरण के लिए, आपको अपनी मशीन में कई ढेर की आवश्यकता होती है: एक पैरामीटर स्टैक, एक डिक्शनरी स्टैक, और निष्पादन स्टैक। वहां से, आप अपने टोकननाइज़र को केवल प्राचीन प्रक्रियाओं के साथ-साथ शब्द निष्पादन के सेट में बांधते हैं, और फिर आपका अधिकांश दुभाषिया पीएस में ही लिखा जाता है।

यदि आप बूस्ट के बारे में बात कर रहे हैं, तो आप सी ++ को देख रहे हैं, जिसका मतलब है कि आप जितनी तेजी से और स्मृति के साथ ढीले नहीं हो सकते हैं, तो आप या तो स्मार्ट पॉइंटर्स का उपयोग करना चाहेंगे या समझ लेंगे जहां आप गुंजाइश करते हैं और उन्हें फेंकने के बजाए वस्तुओं को निपटाने के लिए सावधान रहें, लेकिन यह सामान्य सी ++ सामान है।

वर्तमान में, मैं अपनी कंपनी के लिए .NET में पीडीएफ उपकरण बना देता हूं, लेकिन एक पूर्व जीवन में मैंने एक्रोबैट संस्करण 1-4 पर काम किया, और मैंने जो कुछ बताया है, वह वास्तव में एक्रोबैट ने हुड के तहत किया था (ठीक है, अधिक या कम - यह सी था, सी ++ नहीं, लेकिन यह वही दृष्टिकोण है)।

xref तालिका (या xref स्ट्रीम) के संबंध में, आप इसे पहले पढ़ते हैं - spec आपको बताता है कि यदि आप ईओएफ पर कूदते हैं और वापस स्कैन करते हैं, तो आपको xref तालिका की शुरुआत मिलती है। आप उस पर विश्लेषण करते हैं (जो एक सीएस 101 असाइनमेंट है), ट्रेलर को पार्स करें, अगर किसी को/प्रीव की तलाश करें और कोई और/पूर्व प्रविष्टियों तक दोहराएं। यह आपको वस्तुओं को देखने के लिए एक पूर्ण xref देता है।

लेखन के लिए - ऐसे कई दृष्टिकोण हैं जिन्हें आप ले सकते हैं। सबसे स्पष्ट बात यह है कि जब किसी ऑब्जेक्ट का संदर्भ दिया जाना है, तो आप इसे नवीनतम उपलब्ध xref प्रविष्टि असाइन करके एक नया संदर्भ ऑब्जेक्ट बनाते हैं। जब भी वस्तुएं अन्य वस्तुओं को लिखित रूप में संदर्भित करती हैं, तो वे पूछते हैं कि इन वस्तुओं का संदर्भ दिया गया है या नहीं। यदि वे हैं, तो वे संदर्भ लिखते हैं (यानी, 14 0 R)। जब संदर्भित ऑब्जेक्ट लिखने का समय आता है, तो आपको वर्तमान स्ट्रीम पॉइंटर मिलता है और इसे xref में संग्रहीत करता है, फिर <objnum> <generation> obj <object contents> endobj लिखें। उदाहरण के लिए, एक शब्दकोश में लिखने के लिए मेरे कोड इस तरह दिखता है:

public override ToStream(PdfStreamingContext context) 
{ 
    if (context.HasReference(this)) // is object referenced in xref 
    { 
     PdfUtils.WriteObjectDefinitionBegin(this, context); 
    } 
    context.Writer.Indent(); 
    context.Writer.WriteLine("<<"); 
    WriteContents(context); 
    context.Writer.Exdent(); 
    context.Writer.Writeline(">>"); 
    if (context.HasReference(this)) 
    { 
     PdfUtils.WriteObjectDefinitionEnd(this, context); 
    } 
} 

मैं कुछ भूसा बाहर कटा हुआ है, ताकि आप गेहूं के नीचे देख सकते हैं। संदर्भ एक ऐसा ऑब्जेक्ट है जिसमें एक नई xref तालिका है और साथ ही उन ऑब्जेक्ट्स को लिखने के लिए एक ऑब्जेक्ट भी है जो स्वचालित रूप से उपयुक्त न्यूलाइन अनुशासन, इंडेंटेशन, लाइन रैपिंग, और इसी तरह से संभालती हैं।

आपको क्या देखना चाहिए कि यहां मूल बातें सीधे आगे नहीं हैं, अगर तुच्छ नहीं है। और अब जब आपको खुद से सवाल पूछना चाहिए, "यदि यह मामूली है, तो बाजार में एक्रोबैट के लिए और अधिक गंभीर (गंभीर) प्रतिस्पर्धा नहीं होती है? जवाब यह है कि भले ही यह मामूली है, फिर भी पीडीएफ लिखना आसान है spec अनुपालन और एक्रोबैट उन लोगों में से अधिकांश को संभालने में सक्षम है। वास्तविक चुनौती यह है कि आप कल्पना का सम्मान कर सकें और सुनिश्चित करें कि आप एक शब्दकोश में सभी आवश्यक मूल्यों को शामिल करते हैं और वे सीमा में हैं और अर्थात् सही हैं। नरक, यहां तक ​​कि तिथि का समय भी प्रारूप - जो कि बहुत अच्छी तरह से निर्दिष्ट है - मेरी लाइब्रेरी में विशेष केस कोड का एक साउंड है जहां अन्य लोगों ने इसे रॉयली से खराब कर दिया है। लगातार सही पीडीएफ उत्पन्न करने में सक्षम होना मुश्किल है और पीडीएफ के समुद्र में कचरा खा रहा है दुनिया में कठिन है।

मैं ऐसा करने के बारे में एक पुस्तक लिख सकता हूं (और शायद चाहिए)। हालांकि बहुत सारे फ्रिंज कोड ग्रबबी हैं, समग्र संरचना ure बहुत सुंदर हो सकता है।

tl; dr - यदि आप पीडीएफ के लिए एक रिकर्सिव वंश पार्सर के बारे में सोच रहे हैं, तो आप बहुत मेहनत कर रहे हैं। आपको केवल एक टोकननाइज़र और एक साधारण आरईपीएल चाहिए।

+0

यह वास्तव में दिलचस्प लग रहा है, 2 चीजें मुझे स्पष्ट नहीं हैं: आपके "लाइन द्वारा पढ़ने वाली रेखा + जब आपको" xref' भाग को प्रबंधित करने के तरीके की आवश्यकता होती है "के साथ? एक पीडीएफ लिखने के बारे में कैसे? जब आप बिना कूद के लाइन के लाइन को लिखने की आवश्यकता होती है तो आप लेखन को कैसे प्रबंधित करते हैं? – user2244984

+0

+1 उत्कृष्ट उत्तर, पूरी तरह से सहमत हैं। मुझे आत्मा पसंद है, लेकिन यहां इसे नियोजित नहीं किया जाएगा (ठीक है, हो सकता है कि आत्मा लेक्स के साथ झुकाव के लिए)। @ user2244984 लेखन के लिए, आपके पास हमेशा आउटपुट स्ट्रीम ऑर्डर में ट्रैवर्स के लिए एक समरूप प्रतिनिधित्व होगा (और आवश्यकता होगी)। – sehe

संबंधित मुद्दे